<?php

namespace extend\bridgeserv\client;

use Swoole\Coroutine;
use Swoole\Server;
use zhanshop\App;

class HttpClient
{
    /**
     * @var \extend\bridgeserv\BridgeServerProxy
     */
    protected $manage = null;
    protected $toFd = 0;
    /**
     * @var Coroutine\Socket
     */
    public $socket = null; // 代理最终的目标连接
    public $proxyPass = "";
    public $proxyExpire = 3600;


    public $lastTime = 0; // 最后交互时间

    public function __construct(mixed $manage)
    {
        $this->manage = $manage;
        $this->lastTime = time();
        $this->proxyPass = App::env()->get('PROXY_PASS', 'zhangqiquan@123');
        $this->proxyExpire = intval(App::env()->get('PROXY_EXPIRE', '3600'));
    }

    /**
     * 分解账号密码
     * @param string $data
     * @return string
     * @throws \Exception
     */
    protected function deAccount(string $data)
    {
        $data = urldecode(str_replace('_', '%', $data));
        $decrypt = App::aes()->decrypt($data);
        $arr = explode(',', $decrypt);
        if(count($arr) != 2){
            return "";
        }
        if((intval($arr[1]) + $this->proxyExpire) < time()){
            return "";
        }
        return $arr[0];
    }

    /**
     * 连接并请求
     * @param Server $server
     * @param int $fd
     * @param string $data
     * @return void
     * @throws \Exception
     */
    public function connect(mixed $server, int $fd, string $data)
    {
        $reqindex = strstr($data, "\r", true); // 请求行末尾
        if($reqindex){
            // 认证权限
            preg_match('/Proxy-Authorization: Basic (\S*)/', $data, $match);
            $isPass = false;
            if(isset($match[1])){
                $authInfo = base64_decode($match[1]);
                list($user, $password) = explode(':', $authInfo);
                $user = $this->deAccount($user);
                $this->toFd = $user;
                $password = $this->deAccount($password);
                if($user == false || $password == false){
                    App::log()->push("http代理认证失败");
                    $server->send($fd, "HTTP/1.1 405 Bad Request\r\n\r\n");
                    $server->close($fd);
                    return false;
                }
                $isPass = true;
            }
            // 如果不通过
            if($isPass == false){
                App::log()->push("http代理认证获取失败");
                $server->send($fd, "HTTP/1.1 405 Bad Request\r\n\r\n");
                $server->close($fd);
                return false;
            }
            $reqLine = explode(' ', $reqindex);
            if(count($reqLine) >= 3){
                $addr = [];
                $addr['method'] = $reqLine[0];
                $addr['host'] = $reqLine[1];

                $urlData = parse_url($addr['host']);
                if(($urlData['host'] ?? '') == false || intval($urlData['port'] ?? 80) <= 0){
                    $server->send($fd, "HTTP/1.1 405 Bad Request\r\n\r\n");
                    $server->close($fd);
                    return false;
                }
                $addr['version'] = $reqLine[2];
                $addr['body'] = base64_encode($data);
                // 连接消息
                $server->send($this->toFd, json_encode([
                        'uri' => '/v1/proxy.httpMessage/connect/'.$fd,
                        'header' => [],
                        'body' => $addr
                    ])."\r\n");
                return true;
            }
        }
        $server->send($fd, "HTTP/1.1 405 Bad Request\r\n\r\n");
        $server->close($fd);
        return false;
    }

    /**
     * 转发代理消息
     * @param Server $server
     * @param int $fd
     * @param string $data
     * @return true
     */
    public function send(mixed $server, int $fd, string $data)
    {
        $server->send($this->toFd, json_encode([
                'uri' => '/v1/proxy.httpMessage/message/'.$fd,
                'header' => [],
                'body' => base64_encode($data)
            ])."\r\n");
        return true;
    }

    /**
     * 转发关闭消息
     * @param Server $server
     * @param int $fd
     * @return true
     */
    public function close(mixed $server, int $fd)
    {
        $server->send($this->toFd, json_encode([
                'uri' => '/v1/proxy.httpMessage/close/'.$fd,
                'header' => [],
                'body' => ""
            ])."\r\n");
        return true;
    }
}